from abc import ABCMeta, abstractclassmethod


class PublicFunctionException(Exception):
    """公共方法自定义异常处理"""

    def __init__(self, state, message):
        super().__init__(message)
        self.state = state
        self.message = message


# 根据不同的控件定义一个类来获取其文本内容，后续可以加其他的内容
class FieldContentBase(metaclass=ABCMeta):
    """用于字段展示的一个基类"""

    def __init__(self, control, filed_title, filed_name):
        self.control = control
        self.filed_title = filed_title
        self.filed_name = filed_name

    @property
    def get_text(self):
        """获取文本内容"""
        pass


class QLineEditFiled(FieldContentBase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @property
    def get_text(self):
        return self.control.text().strip()  # 返回的是字符串或者空


class QCheckBoxFiled(FieldContentBase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @property
    def get_text(self):
        return self.control.isChecked()  # 返回的是布尔值


class QComboBoxFiled(FieldContentBase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @property
    def get_text(self):
        return self.control.currentText()  # 返回的是字符串


class QTableWidgetFiled(FieldContentBase):
    def __init__(self, row_column, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.row_column = row_column  # 注意传递参数的顺序， row_column需要作为第一个参数传入

    @property
    def get_text(self):
        if (isinstance(self.row_column, list) or isinstance(self.row_column, tuple)) and len(self.row_column) == 2:
            item = self.control.item(*self.row_column)
            if item:
                return item.text().strip()  # 返回的是字符串
            else:
                return None
        raise PublicFunctionException(state=2000, message='传递的行列坐标不符合要求！')


# 自定义一个车辆信息的基类
class VehicleInfoBase(metaclass=ABCMeta):
    # 每个车的基本信息
    def __init__(self, plate_number, plate_type, vin, serial_number, vehicle_type, inspection_category,
                 vehicle_class, vehicle_model, total_mass, brand, body_color, number_axes, vehicle_owner,
                 date_first_registration, manufacturer):
        self.plate_number = plate_number
        self.plate_type = plate_type
        self.vin = vin
        self.serial_number = serial_number
        self.vehicle_type = vehicle_type
        self.inspection_category = inspection_category
        self.vehicle_class = vehicle_class
        self.vehicle_model = vehicle_model
        self.total_mass = total_mass
        self.brand = brand
        self.body_color = body_color
        self.number_axes = number_axes
        self.vehicle_owner = vehicle_owner
        self.date_first_registration = date_first_registration
        self.manufacturer = manufacturer
        self.not_null_item_list = [self.plate_type, self.vin, self.serial_number, self.vehicle_type,
                                   self.inspection_category, self.vehicle_class,
                                   self.total_mass]  # 设置不可为空的项目列表

    @staticmethod
    def detection_text_is_null(filed_object: FieldContentBase):
        """检测获取的文本是否为空，以字典的形式返回，为空is_null=True"""
        content = filed_object.get_text
        if not content:
            return {'filed_name': filed_object.filed_name, 'filed_title': filed_object.filed_title, 'is_null': True}
        return {'filed_name': filed_object.filed_name, 'filed_title': filed_object.filed_title, 'is_null': False}

    def required_item(self, filed_object_list):
        """必填项目,如果他有空就会进行返回"""
        null_item_list = []  # 有空字段就加入到列表中
        for filed_object in filed_object_list:
            filed_info_dict = self.detection_text_is_null(filed_object)
            if filed_info_dict.get('is_null'):
                null_item_list.append(filed_info_dict)
        return null_item_list

    def add_not_null_item(self, filed_object):
        """添加必填项目字段，可以是列表，可以是单个字段"""
        if isinstance(filed_object, list):
            self.not_null_item_list.extend(filed_object)
        else:
            self.not_null_item_list.append(filed_object)

    def remove_not_null_item(self, filed_object: FieldContentBase):
        """移除必填字段，只能是某个字段"""
        self.not_null_item_list.remove(filed_object)

    def detection_required_item(self):
        """检测必填的项目"""
        return self.required_item(self.not_null_item_list)  # 传递是增加减少后的项目


class CompleteVehicleInfo(VehicleInfoBase):
    """整车的基本信息"""

    def __init__(self, vehicle_standard_length=None, vehicle_standard_width=None, limit_length=None, limit_width=None,
                 limit_height=None,
                 vehicle_standard_height=None, vehicle_standard_wheelbase=None, vehicle_standard_fence_depth=None,
                 vehicle_standard_curb_weigh=None, cargo_hold_standard_length=None, cargo_hold_standard_width=None,
                 cargo_hold_standard_height=None, tank_standard_length=None, tank_standard_width=None,
                 tank_standard_height=None, tank_standard_volume=None, tank_standard_angle=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.vehicle_standard_length = vehicle_standard_length
        self.vehicle_standard_width = vehicle_standard_width
        self.vehicle_standard_height = vehicle_standard_height
        self.limit_length = limit_length
        self.limit_width = limit_width
        self.limit_height = limit_height
        self.vehicle_standard_wheelbase = vehicle_standard_wheelbase
        self.vehicle_standard_fence_depth = vehicle_standard_fence_depth
        self.vehicle_standard_curb_weigh = vehicle_standard_curb_weigh
        self.cargo_hold_standard_length = cargo_hold_standard_length
        self.cargo_hold_standard_width = cargo_hold_standard_width
        self.cargo_hold_standard_height = cargo_hold_standard_height
        self.tank_standard_length = tank_standard_length
        self.tank_standard_width = tank_standard_width
        self.tank_standard_height = tank_standard_height
        self.tank_standard_volume = tank_standard_volume
        self.tank_standard_angle = tank_standard_angle


class PrimeMoverInfo(VehicleInfoBase):
    """牵引车的基本信息"""

    def __init__(self, vehicle_standard_length, vehicle_standard_width,
                 vehicle_standard_height, vehicle_standard_wheelbase, vehicle_standard_curb_weigh, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.vehicle_standard_length = vehicle_standard_length
        self.vehicle_standard_width = vehicle_standard_width
        self.vehicle_standard_height = vehicle_standard_height
        self.vehicle_standard_wheelbase = vehicle_standard_wheelbase
        self.vehicle_standard_curb_weigh = vehicle_standard_curb_weigh


# 自定义一个检验项目基类
class InspectionCategory(metaclass=ABCMeta):
    def __init__(self, actual, standard=None, tolerance=None):
        """:@param actual:检测值，不能为空，为必传项目
        :@param standard:标准值， 可为空，为空表示不计算
        :@param tolerance:判定范围， 为空表示不计算"""
        self.actual = actual
        self.standard = standard
        self.tolerance = tolerance  # 是元组或者列表

    def calculate_difference(self):
        """定义一个计算差值的方法"""
        return self.actual - self.standard

    def calculate_percentage(self):
        """定义一个计算百分比的方法"""
        return round((self.actual - self.standard) / self.standard * 100, 2) if self.standard != 0 else 0

    def is_within_tolerance(self):
        """根据判定范围判断是否合格"""
        difference = self.calculate_difference()  # 差值
        percentage = self.calculate_percentage()  # 百分比
        return abs(difference) <= self.tolerance[0] or abs(percentage) <= self.tolerance[1]

    def integrate_result(self):
        """将结果整个在一起"""
        if self.standard:
            return [(self.standard, self.actual, self.calculate_difference(), self.calculate_percentage(),
                     self.is_within_tolerance())]
        return [tuple(self.actual)]


# 定义一个外廓项目类
class OverallDimensionsInspectionItem(InspectionCategory):
    """用于外廓项目检验使用"""

    def __init__(self, limit=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.limit = limit

    def calculate_difference(self):
        """重写计算差值的方法，因为传递进来的是列表"""
        return [actual_value - standard_value for actual_value, standard_value in
                zip(self.actual, self.standard)]

    def calculate_percentage(self):
        """重写计算百分比的方法，返回的是列表"""
        return [
            round((actual_value - standard_value) / standard_value * 100, 2) if int(
                standard_value) != 0 else 0
            for actual_value, standard_value in zip(self.actual, self.standard)]

    def is_within_tolerance(self):
        """根据判定判断是否合格"""
        difference = self.calculate_difference()
        percentage = self.calculate_percentage()
        if self.limit:
            return [((abs(d) <= tol[0]) or (abs(p) <= tol[1])) and actual <= limit for d, p, tol, limit, actual in
                    zip(difference, percentage, self.tolerance, self.limit, self.actual)]
        return [abs(d) <= tol[0] or abs(p) <= tol[1] for d, p, tol, actual in
                zip(difference, percentage, self.tolerance, self.actual)]

    def integrate_result(self):
        """将结果整个在一起"""
        if self.standard:
            return [(standard, actual, diff, per, judge) for actual, standard, diff, per, judge in
                    zip(self.actual, self.standard, self.calculate_difference(),
                        self.calculate_percentage(), self.is_within_tolerance())]
        return [tuple(self.actual)]


class WheelbaseInspectionItem(InspectionCategory):
    pass


class FenceInspectionItem(InspectionCategory):
    pass


class CurbWeighInspectionItem(InspectionCategory):
    pass


class CargoBoxInspectionItem(InspectionCategory):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def calculate_difference(self):
        """重写计算差值的方法，因为传递进来的是列表"""
        return [actual_value - standard_value for actual_value, standard_value in
                zip(self.actual, self.standard)]

    def calculate_percentage(self):
        """重写计算百分比的方法，返回的是列表"""
        return [
            round((actual_value - standard_value) / standard_value * 100, 2) if int(
                standard_value) != 0 else 0
            for actual_value, standard_value in zip(self.actual, self.standard)]

    def is_within_tolerance(self):
        """根据判定判断是否合格"""
        difference = self.calculate_difference()
        percentage = self.calculate_percentage()
        return [abs(d) <= tol[0] or abs(p) <= tol[1] for d, p, tol, actual in
                zip(difference, percentage, self.tolerance, self.actual)]

    def integrate_result(self):
        """将结果整个在一起"""
        if self.standard:
            return [(standard, actual, diff, per, judge) for actual, standard, diff, per, judge in
                    zip(self.actual, self.standard, self.calculate_difference(),
                        self.calculate_percentage(), self.is_within_tolerance())]
        return [tuple(self.actual)]


class TankBodyInspectionItem(CargoBoxInspectionItem):
    def calculate_difference(self):
        """重写计算差值的方法，因为传递进来的是列表"""
        return [round(actual_value - standard_value, 2) for actual_value, standard_value in
                zip(self.actual, self.standard)]
